#include <omnetpp.h>
#include "LT3LCG.h"

Register_PerRunConfigOption(CFGID_SEED_LT3LCG, "seed-%-lt3lcg", CFG_DOUBLE, "1234.", "When LT3LCG is selected as random number generator: seed for RNG number k. (Substitute k for '%' in the key.)");
Register_PerRunConfigOption(CFGID_MULTIPLIER_LT3LCG, "multiplier-%-lt3lcg", CFG_DOUBLE, "25214903917.", "When LT3LCG is selected as random number generator: multiplier (25214903917 as default) for RNG number k. (Substitute k for '%' in the key.)");
Register_PerRunConfigOption(CFGID_ADDITIVE_LT3LCG, "additive-%-lt3lcg", CFG_DOUBLE, "11.", "When LT3LCG is selected as random number generator: additive (11 as default) for RNG number k. (Substitute k for '%' in the key.)");
Register_PerRunConfigOption(CFGID_MODULE_LT3LCG, "module-%-lt3lcg", CFG_DOUBLE, "281474976710656.", "When LT3LCG is selected as random number generator: module (281474976710656 as default) for RNG number k. (Substitute k for '%' in the key.)");

using std::cout;

// The class needs to be registered with OMNeT
Register_Class(LT3LCG);

void LT3LCG::initialize(int seedSet, int rngId, int numRngs, int parsimProcId, int parsimNumPartitions, cConfiguration *cfg) {

	// Drand48() default values
	char key[32];

	sprintf(key,  "seed-%d-lt3lcg", rngId);
	const char *value = cfg->getConfigValue(key);
	if (value==NULL) seed = 1234.;
	else seed = cConfiguration::parseDouble(value,"","1234.");

	sprintf(key,  "multiplier-%d-lt3lcg", rngId);
	value = cfg->getConfigValue(key);
	if (value==NULL) multiplier = 25214903917.;
	else multiplier = cConfiguration::parseDouble(value,"","25214903917.");

	sprintf(key,  "additive-%d-lt3lcg", rngId);
	value = cfg->getConfigValue(key);
	if (value==NULL) additive = 11.;
	else additive = cConfiguration::parseDouble(value,"","11.");

	sprintf(key,  "module-%d-lt3lcg", rngId);
	value = cfg->getConfigValue(key);
	if (value==NULL) module = 281474976710656.;
	else module = cConfiguration::parseDouble(value,"","281474976710656.");

	// Setting init values
	numDrawn = 0;
	currentX = seed;

	// Display the Omnet envir init values
	cout << "LCG random generator with Id = " << rngId << " loaded with:" << endl;
	cout << "\tmultiplier: " << multiplier << endl;
	cout << "\tadditive: " << additive << endl;
	cout << "\tmodule: " << module << endl;
	cout << "\tseed: " << seed << endl;
}

unsigned long LT3LCG::numbersDrawn() {

	return numDrawn;
}

void LT3LCG::selfTest() {

	cout << "Initializing the Linear Congruential Random Generator" << endl;
}

unsigned long LT3LCG::intRand() {

	numDrawn++;
	currentX = multiplier * currentX + additive;
	currentX = fmod (currentX,module);
	return (unsigned long) currentX;
}

double LT3LCG::doubleRand() {

	intRand();
	return currentX / module;
}

unsigned long LT3LCG::intRand(unsigned long n) {
	if (n >= intRandMax())
		return intRand();
	else
		return (unsigned long) ( doubleRand() * ((double) n));
}

unsigned long LT3LCG::intRandMax() {

	return (unsigned long) module-1;
}

double LT3LCG::doubleRandNonz() {

	double num;
	while ((num = doubleRand()) == 0);
	return num;
}

double LT3LCG::doubleRandIncl1() {
	return doubleRand();
}

double LT3LCG::doubleRandNonzIncl1() {
	return doubleRand();
}
